﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using IronBrokers.ClientManager.Exceptions;
using IronBrokers.ClientManager.Model;
using IronBrokers.ClientManager.Helpers;

namespace IronBrokers.ClientManager.Database
{
    public class PersistenceManager : IPersistenceManager
    {
        #region Protected properties
        protected EntitiesContainer Context { get; private set; } 
        #endregion

        #region Constructor
        public PersistenceManager()
        {
            Context = new EntitiesContainer();
        } 
        #endregion

        #region UpdateStockValue
        public void UpdateStockValue(Model.StockData updateData)
        {
            if (String.IsNullOrEmpty(updateData.GoodName))
            {
                throw new ArgumentException("Goods name cannot be empty!");
            }

            if (String.IsNullOrEmpty(updateData.IndexName))
            {
                throw new ArgumentException("Index name cannot be empty!");
            }

            Index indexEnt = Context.Indexes.Where(x => x.Identity.Equals(updateData.IndexName)).FirstOrDefault();
            if (indexEnt == null)
            {
                indexEnt = new Index()
                {
                    Identity = updateData.IndexName
                };
            }

            Good goodEnt = indexEnt.Goods.Where(x => x.Identity.Equals(updateData.GoodName)).FirstOrDefault();
            if (goodEnt == null)
            {
                goodEnt = new Good()
                {
                    Identity = updateData.GoodName
                };
            }

            Quotation quotationEnt = new Quotation()
            {
                Timestamp = DateTime.Now,
                Value = updateData.Value
            };

            goodEnt.Quotations.Add(quotationEnt);
            indexEnt.Goods.Add(goodEnt);
            if (indexEnt.Id == 0)
            {
                Context.Indexes.AddObject(indexEnt);
            }
            Context.SaveChanges();
        } 
        #endregion

        #region RegisterUser
        public void RegisterUser(string userName, string passwordSH1)
        {
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentException("Username cannot be empty!");
            }

            bool userExists = Context.Users.Count(x => x.Name.Equals(userName)) == 1;
            if (userExists)
            {
                throw new UserAlreadyExistsException(String.Format("User '{0}' already exists!", userName));
            }

            User user = new User()
            {
                Name = userName,
                PasswdSH1 = passwordSH1,
                IsSubscribed = false,
                LastSubscription = DateTime.Now
            };

            Context.Users.AddObject(user);
            Context.SaveChanges();
        } 
        #endregion

        #region UnregisterUser
        public void UnregisterUser(string userName)
        {
            User userEnt = Context.Users.Where(x => x.Name.Equals(userName)).FirstOrDefault();
            if (userEnt != null)
            {
                Context.Users.DeleteObject(userEnt);
                Context.SaveChanges();
            }
            else
            {
                throw new UserDoesNotExistException(String.Format("User '{0}' does not exist", userName));
            }
        } 
        #endregion

        #region SetUserPriviledges
        public void SetUserPriviledges(string userName, List<string> priviledges)
        {
            if (priviledges.Distinct().Count() != priviledges.Count())
            {
                throw new PriviledgeAlreadyExistsException("Priviledge already exists!");
            }
            User userEnt = Context.Users.Where(x => x.Name.Equals(userName)).FirstOrDefault();
            if (userEnt != null)
            {
                foreach (String topic in priviledges)
                {
                    if (userEnt.Priviledges.Count(x => x.Topic.Equals(topic)) == 0) {
                        userEnt.Priviledges.Add(new Priviledge()
                        {
                            Topic = topic
                        });
                    }
                }

                IEnumerable<Priviledge> toDeletePrivs = userEnt.Priviledges.Where(x => !priviledges.Contains(x.Topic)).ToArray();
                foreach (var priv in toDeletePrivs)
                {
                    Context.Priviledges.DeleteObject(priv);
                }
                Context.SaveChanges();
            }
            else
            {
                throw new UserDoesNotExistException(String.Format("User '{0}' does not exist", userName));
            }
        } 
        #endregion

        #region GetUserList
        public List<Model.User> GetUserList()
        {
            List<Model.User> users = new List<Model.User>();
            foreach (var dbUser in Context.Users)
            {
                Model.User user = new Model.User()
                {
                    Name = dbUser.Name,
                    Priviledges = new List<string>()
                };
                foreach (var priviledge in dbUser.Priviledges)
                {
                    user.Priviledges.Add(priviledge.Topic);
                }
                users.Add(user);
            }
            return users;
        } 
        #endregion

        #region Subscribe
        public Model.Ticket Subscribe(string userName, string password)
        {
            String passwordSH1 = PasswordHelper.SH1(password);
            User dbUser = Context.Users.Where(x => x.Name.Equals(userName) && x.PasswdSH1.Equals(passwordSH1)).FirstOrDefault();
            if (dbUser != null)
            {
                Guid ticketID = Guid.NewGuid();
                dbUser.IsSubscribed = true;
                dbUser.LastSubscription = DateTime.Now;
                dbUser.TicketID = ticketID;
                Context.SaveChanges();

                Model.Ticket ticket = new Model.Ticket()
                {
                    ExchangeName = String.Format(DataFormat.UserExchangeName, userName),
                    QueueName = String.Format(DataFormat.UserQueueName, userName),
                    VhostName = String.Format(DataFormat.UserVhostName, userName),
                    UserName = userName,
                    Url = DataFormat.RabbitMQAddress,
                    TicketID = ticketID
                };

                return ticket;
            }
            else
            {
                throw new UnauthorizedAccessException(String.Format("User '{0}' has not been registered!", userName));
            }
        }
        
        #endregion

        #region Unsubscribe
        public Model.Ticket Unsubscribe(Guid ticketID)
        {
            User dbUser = Context.Users.Where(x => ticketID.Equals(x.TicketID)).FirstOrDefault();
            if (dbUser != null)
            {
                dbUser.IsSubscribed = false;
                dbUser.TicketID = null;
                Context.SaveChanges();

                Model.Ticket ticket = new Model.Ticket()
                {
                    ExchangeName = String.Format(DataFormat.UserExchangeName, dbUser.Name),
                    QueueName = String.Format(DataFormat.UserQueueName, dbUser.Name),
                    VhostName = String.Format(DataFormat.UserVhostName, dbUser.Name),
                    UserName = dbUser.Name,
                    Url = DataFormat.RabbitMQAddress,
                    TicketID = ticketID
                };

                return ticket;
            }
            else
            {
                throw new UnauthorizedAccessException(String.Format("Ticket is invalid!"));
            }
        } 
        #endregion

    }
}